#include "udpServer.hpp"
#include <unordered_map>
#include <fstream>
#include <signal.h>
#include "onlineUser.hpp"

using namespace Server;

std::string dictPATH = "./dict.txt";
std::unordered_map<std::string, std::string> dict;

//demo 1
static bool cutString(const std::string& target, std::string* key, std::string* val)
{
    std::string step = ":";
    size_t pos = target.find(step);
    if (pos != std::string::npos)
    {
        *key = target.substr(0, pos);
        *val = target.substr(pos + step.size());
        return true;
    }

    return false;
}

static void debugPrint()
{
    for (auto& dt : dict)
    {
        std::cout << dt.first << " : " << dt.second << std::endl;
    }
}

static void initDict()
{
    std::ifstream in(dictPATH, std::ios::binary);
    if (!in.is_open())
    {
        std::cerr << "open file error : " << OPEN_ERROR << std::endl;
        exit(OPEN_ERROR);
    }

    std::string line;
    std::string key;
    std::string val;

    while (getline(in, line))
    {
        if (cutString(line, &key, &val))
        {
            dict.insert({key, val});
        }
    }

    in.close();

    std::cout << "dict load succes" << std::endl;
}

void handler(const std::string& ip, const uint16_t port, const std::string& masage, const int sockfd = 0)
{
    std::string response_masage;
    auto it = dict.find(masage);
    if (it != dict.end())
    {
        response_masage = it->second;
    }
    else
    {
        response_masage = "no found";
    }

    struct sockaddr_in client;
    bzero(&client, sizeof(client));

    client.sin_family = AF_INET;
    client.sin_port = htons(port);
    client.sin_addr.s_addr = inet_addr(ip.c_str());

    sendto(sockfd, response_masage.c_str(), response_masage.size(), 0, (struct sockaddr *)&client, sizeof(client));
}

//demo 2
void loadDict(int)
{
    initDict();
    // debugPrint();
}

void execCommond(const std::string& ip, const uint16_t port, const std::string& commond, const int sockfd = 0)
{
    if (commond.find("rm") != string::npos 
        || commond.find("mv") != string::npos 
        || commond.find("rmdir") != string::npos 
        || commond.find("kill") != string::npos)
    {
        cout << ip << "[" << port << "] # 非法操作 " << endl;
        return;
    }

    std::string response;
    //执行操作
    FILE* fp = popen(commond.c_str(), "r"); 
    // 1. 建立管道pipe
    // 2. 创建子进程
    // 3. exec* 系列执行指令
    //创建时，返回文件指针，文件内容可以以读方式打开or写方式打开

    if (fp == nullptr)
    {
        response = commond + " exec fail ";
    }

    char line[1024] = "";

    while (fgets(line, sizeof(line), fp))
    {
        response += line;
    }
    pclose(fp);

    //返回操作结果
    struct sockaddr_in client;
    bzero(&client, sizeof(client));

    client.sin_family = AF_INET;
    client.sin_port = htons(port);
    client.sin_addr.s_addr = inet_addr(ip.c_str());

    sendto(sockfd, response.c_str(), response.size(), 0, (struct sockaddr *)&client, sizeof(client));
}

//demo 3

onlineUser online;

void chatWithEvery(const std::string& ip, const uint16_t port, const std::string& chatMsg, const int sockfd = 0)
{
    if (chatMsg == "online")
    {
        online.addUser(ip, port);
    }
    if (chatMsg == "offline")
    {
        online.delUser(ip, port);
    }
    if (online.isOnline(ip, port))
    {
        online.broadcastMassage(sockfd, chatMsg);
    }
    else 
    {
        std::string response = "你还没有上线，请先上线，输入 \"online\" 上线";

        struct sockaddr_in client;
        bzero(&client, sizeof(client));

        client.sin_family = AF_INET;
        client.sin_port = htons(port);
        client.sin_addr.s_addr = inet_addr(ip.c_str());

        sendto(sockfd, response.c_str(), response.size(), 0, (struct sockaddr *)&client, sizeof(client));
    }
}

//demo 4
// Server linux
// Client windows
void windowsMasg(const std::string& ip, const uint16_t port, const std::string& masg, const int sockfd = 0)
{
    std::string response_masage = "[Server echo]" + masg;

    struct sockaddr_in client;
    bzero(&client, sizeof(client));

    client.sin_family = AF_INET;
    client.sin_port = htons(port);
    client.sin_addr.s_addr = inet_addr(ip.c_str());

    sendto(sockfd, response_masage.c_str(), response_masage.size(), 0, (struct sockaddr *)&client, sizeof(client));
}

inline void Usage(std::string proc)
{
    std::cout << "Usage : \n\t" << proc << " local_port\n\n";
}

int main(int argc, char* argv[])
{
    if (argc != 2)
    {
        Usage(argv[0]);
        exit(USAGE_ERROR);
    }

    uint16_t port = atoi(argv[1]);
    //std::string ip = argv[1];

    // initDict();
    // debugPrint();
    // signal(SIGINT, loadDict);

    // std::unique_ptr<udpServer> userv(new udpServer(handler, port));
    // std::unique_ptr<udpServer> userv(new udpServer(execCommond, port));
    // std::unique_ptr<udpServer> userv(new udpServer(chatWithEvery, port));
    std::unique_ptr<udpServer> userv(new udpServer(windowsMasg, port));


    userv->initServer();
    userv->start();

    return 0;
}